﻿#include "CameraTUCAM_private.h"
#include "../CameraCommon/DialogCameraSetup.h"
#include "./sdk/inc/TUCamApi.h"

#include "ImageThread.h"
#include "CameraTUCAMImageProcessing.h"

namespace Qly {

static AutoInit * pAutoInitObject = nullptr;


AutoInit::AutoInit()
{
    itApi.pstrConfigPath = NULL;
    itApi.uiCamCount = 0;
    if (TUCAMRET_SUCCESS != TUCAM_Api_Init(&itApi))
    {
        qCritical() << "TUCAM_Api_Init() failed.";
    }
    if(itApi.uiCamCount == 0)
    {
        qDebug() << "Cannot find TUCSEN Camera!";
    }
    qDebug() << "TUCAM_Api_Init() Success! Find " << itApi.uiCamCount << " Cameras.";
}

AutoInit::~AutoInit()
{
    TUCAM_Api_Uninit();
}

void AutoInit::cameraDeleted()
{
    cameraClassCount --;
    if(cameraClassCount == 0 && factoryClassCount == 0)
    {
        delete pAutoInitObject;
        pAutoInitObject = nullptr;
    }
}

void AutoInit::factoryDeleted()
{
    factoryClassCount --;
    if(cameraClassCount == 0 && factoryClassCount == 0)
    {
        delete pAutoInitObject;
        pAutoInitObject = nullptr;
    }
}

AutoInit *getAutoInitInstance()
{
    if(pAutoInitObject == nullptr)
    {
        pAutoInitObject = new AutoInit;
    }
    return pAutoInitObject;
}

QString CameraTUCAM_p::cameraName()
{

    TUCAM_VALUE_INFO valInfo;

    // 获取相机类型
    valInfo.nID = TUIDI_CAMERA_MODEL;
    if (TUCAMRET_SUCCESS == TUCAM_Dev_GetInfo(m_opCam.hIdxTUCam, &valInfo))
    {
        m_DeviceModelName = QString(valInfo.pText);
    }

    //读取SN值
    char cSN[TUSN_SIZE] = {0};

    TUCAM_REG_RW regRW;
    regRW.nRegType= TUREG_SN;
    regRW.pBuf    = &cSN[0];
    regRW.nBufSize= TUSN_SIZE;
    if (TUCAMRET_SUCCESS == TUCAM_Reg_Read(m_opCam.hIdxTUCam, regRW))
    {
        m_DeviceID = QString(cSN);
    }

    return m_DeviceModelName;
}



bool CameraTUCAM_p::openCameraByID(QString id)
{
    AutoInit * p = getAutoInitInstance();
    unsigned cnt = p->cameraCount();
    for(int i = 0; i < cnt; i++)
    {
        TUCAM_Dev_Open(&m_opCam);

        //读取SN值
        char cSN[TUSN_SIZE] = {0};

        TUCAM_REG_RW regRW;
        regRW.nRegType= TUREG_SN;
        regRW.pBuf    = &cSN[0];
        regRW.nBufSize= TUSN_SIZE;
        if (TUCAMRET_SUCCESS == TUCAM_Reg_Read(m_opCam.hIdxTUCam, regRW))
        {
            QString name = QString(cSN);
            if(id == name)
            {
                CameraTUCAMImageProcessing* p = (CameraTUCAMImageProcessing*)m_cameraImageProcessing;
                p->m_opCam = m_opCam;
                return true;
            }
        }
        TUCAM_Dev_Close(m_opCam.hIdxTUCam);
    }
    return false;
}

bool CameraTUCAM_p::openByIdx(unsigned int index)
{
    m_opCam.uiIdxOpen = index;
    if(TUCAMRET_SUCCESS != TUCAM_Dev_Open(&m_opCam) )
    {
        return false;
    }
    CameraTUCAMImageProcessing* p = (CameraTUCAMImageProcessing*)m_cameraImageProcessing;
    cameraName();
    p->m_opCam = m_opCam;
    qDebug() << "CameraTUCAM::openByIdx() Success!";
    return true;
}

bool CameraTUCAM_p::openDefaultCamera()
{
    return openByIdx(0);
}


AVPixelFormat CameraTUCAM_p::getBayer8Patern()
{
    return AV_PIX_FMT_GRAY8;
}

bool CameraTUCAM_p::setPixelType(AVPixelFormat type)
{
    return false;
}

void CameraTUCAM_p::close()
{
    if (NULL != m_opCam.hIdxTUCam)
    {
        TUCAM_Dev_Close(m_opCam.hIdxTUCam);
    }
    m_DeviceModelName.clear();
}


bool CameraTUCAM_p::saveSettingsToFile(QString fileName)
{
    TUCAMRET ret = TUCAM_File_SaveProfiles(m_opCam.hIdxTUCam, fileName.toLocal8Bit().data());
    if(ret != TUCAMRET_SUCCESS)
    {
        qWarning() << "TUCAM_File_SaveProfiles failed";
        return false;
    }
    return true;
}

bool CameraTUCAM_p::loadSettingsFromFile(QString fileName)
{
    TUCAMRET ret = TUCAM_File_LoadProfiles(m_opCam.hIdxTUCam, fileName.toLocal8Bit().data());
    if(ret != TUCAMRET_SUCCESS)
    {
        qWarning() << "TUCAM_File_LoadProfiles failed";
        return false;
    }
    return true;
}

bool CameraTUCAM_p::isLive()
{
    return m_isLive;
}

bool CameraTUCAM_p::startGrabbing()
{
    m_imageCount = 0;
    m_frame.pBuffer = 0;
    m_frame.ucFormatGet = TUFRM_FMT_RGB888;
    m_frame.uiRsdSize = 1;

    //TUCAM_Buf_DataCallBack(m_opCam.hIdxTUCam, OnCallbackBuffer, this);
    TUCAMRET ret = TUCAM_Capa_SetValue(m_opCam.hIdxTUCam,TUIDC_TESTIMGMODE, 1);

    if(ret != TUCAMRET_SUCCESS)
    {
        qDebug() << "TUCAM_Capa_SetValue(m_opCam.hIdxTUCam,TUIDC_TESTIMGMODE, 1) failed";
    }
    TUCAM_Buf_Alloc(m_opCam.hIdxTUCam, &m_frame);
    if( TUCAM_Cap_Start(m_opCam.hIdxTUCam, TUCCM_SEQUENCE) == TUCAMRET_SUCCESS)
    {
        m_isLive = true;
        m_pThread->start();
    }
    //pImageBuffer = new uint8_t[4096 * 4096 * 4];
    return true;
}


CameraTUCAM_p::CameraTUCAM_p(QObject *parent)
    :IndustryCamera(parent),
    pImageBuffer(nullptr)
{
    AutoInit * p = getAutoInitInstance();
    p->cameraCreated();

    m_dialog = new DialogCameraSetup(this);
    m_isLive = false;

    m_cameraImageProcessing = new CameraTUCAMImageProcessing;

    m_pThread = new ImageThread(this);
    connect(m_pThread, &QThread::finished, this, &CameraTUCAM_p::on_imageThreadFinished);

    //    m_dia->show();
}

CameraTUCAM_p::~CameraTUCAM_p()
{
    close();
    m_pThread->deleteLater();
    if(pImageBuffer)
    {
        delete [] pImageBuffer;
    }
    delete m_dialog;
    AutoInit * p = getAutoInitInstance();
    p->cameraDeleted();
}

bool CameraTUCAM_p::frameRate(double &fps)
{
    if (TUCAMRET_SUCCESS != TUCAM_Prop_GetValue(m_opCam.hIdxTUCam, TUIDP_FRAME_RATE, &fps) )
    {
        qDebug() <<  "TUCAM_Prop_GetValue(m_opCam.hIdxTUCam, TUIDP_FRAME_RATE, &fps) failed";
        return false;
    }
    return true;
}

bool CameraTUCAM_p::setFrameRate(double fps)
{
    if (TUCAMRET_SUCCESS != TUCAM_Prop_SetValue(m_opCam.hIdxTUCam, TUIDP_FRAME_RATE, fps) )
    {
        qDebug() <<  "TUCAM_Prop_SetValue(m_opCam.hIdxTUCam, TUIDP_FRAME_RATE, fps) failed";
        return false;
    }
    return true;
}

bool CameraTUCAM_p::exposureTimeRange(double &min, double &max, double &fValue)
{
    TUCAM_PROP_ATTR attrProp;
    attrProp.nIdxChn = 0; // Current channel
    attrProp.idProp = TUIDP_EXPOSURETM;

    if (TUCAMRET_SUCCESS == TUCAM_Prop_GetAttr(m_opCam.hIdxTUCam, &attrProp))
    {
        // Exposure range
        min = attrProp.dbValMin * 1000.0; // Minimum exposure, convert exposure time to us from ms.
        max = attrProp.dbValMax * 1000.0; // Maximum exposure
        fValue = attrProp.dbValDft * 1000.0; // Default exposure
        //attrProp.dbValStep; // Exposure time step
    };

    if (TUCAMRET_SUCCESS == TUCAM_Prop_GetValue(m_opCam.hIdxTUCam, TUIDP_EXPOSURETM,  &fValue))
    {
        fValue = fValue * 1000.0;
        return true;
    }
    return false;
}

bool CameraTUCAM_p::setResolutionByIdx(int N)
{
    TUCAMRET ret = TUCAM_Capa_SetValue(m_opCam.hIdxTUCam, TUIDC_RESOLUTION, N);
    if(ret != TUCAMRET_SUCCESS)
    {
        qWarning() << "TUCAM_Capa_SetValue(m_opCam.hIdxTUCam, TUIDC_RESOLUTION, N) failed";
    }
    return ret == TUCAMRET_SUCCESS;
}

QStringList CameraTUCAM_p::resolution()
{
    QStringList list;
    TUCAM_CAPA_ATTR capa;
    capa.idCapa = TUIDC_RESOLUTION;

    if (TUCAMRET_SUCCESS == TUCAM_Capa_GetAttr(m_opCam.hIdxTUCam, &capa))
    {
        int nCnt = capa.nValMax - capa.nValMin + 1;

        char szRes[64] = { 0 };
        TUCAM_VALUE_TEXT valText;
        valText.nTextSize = 64;
        valText.pText = &szRes[0];
        valText.nID = TUIDC_RESOLUTION;

        for (int j = 0; j < nCnt; ++j)
        {
            valText.dbValue = j;
            //TUCAMRET n = TUCAM_Capa_GetValueText(m_opCam.hIdxTUCam, &valText);
            if (TUCAMRET_SUCCESS == TUCAM_Capa_GetValueText(m_opCam.hIdxTUCam, &valText))
            {
                list  << QString(valText.pText);
            }
        }
    }

    return list;
}

bool CameraTUCAM_p::imageSize(QSize &size, QSize &max, QSize &min)
{
    return false;
}

bool CameraTUCAM_p::setExposureTimeMode(AutoExposureMode mode)
{
    TUCAMRET nRet;
    switch (mode) {
    case OFF:
        nRet = TUCAM_Capa_SetValue(m_opCam.hIdxTUCam, TUIDC_ATEXPOSURE, 0);
        break;
    case ONCE:
        return false;
        //TUCAM_Capa_SetValue(m_opCam.hIdxTUCam, TUIDC_ATEXPOSURE, 2);
        break;
    case CONTINUOUS:
        nRet = TUCAM_Capa_SetValue(m_opCam.hIdxTUCam, TUIDC_ATEXPOSURE, 1);
        break;
    }
    return nRet == TUCAMRET_SUCCESS;
}

bool CameraTUCAM_p::exposureTimeMode(AutoExposureMode &mode)
{
    int nVal = 0;
    if( TUCAM_Capa_GetValue(m_opCam.hIdxTUCam, TUIDC_ATEXPOSURE, &nVal) == TUCAMRET_SUCCESS)
    {
        switch(nVal)
        {
        case 0:
            mode = AutoExposureMode::OFF;
            break;

        default:
            mode = AutoExposureMode::CONTINUOUS;
            break;
        }
        return true;
    }
    mode = AutoExposureMode::OFF;
    return false;
}


bool CameraTUCAM_p::exposureTime(double &us)
{
    if( TUCAM_Prop_GetValue(m_opCam.hIdxTUCam, TUIDP_EXPOSURETM, &us, 0) == TUCAMRET_SUCCESS)
    {
        us = us * 1000.0;
        return true;
    }
    us = 0;
    return false;
}

bool CameraTUCAM_p::setExposureTime(double us)
{
    //关闭自动曝光
    int nVal;
    if (TUCAMRET_SUCCESS == TUCAM_Capa_GetValue(m_opCam.hIdxTUCam, TUIDC_ATEXPOSURE, &nVal))
    {
        if (nVal != 0)
        {
            printf("Close auto exposure.\n");
            TUCAM_Capa_SetValue(m_opCam.hIdxTUCam, TUIDC_ATEXPOSURE, 0);
        }
    }

    return TUCAM_Prop_SetValue(m_opCam.hIdxTUCam, TUIDP_EXPOSURETM, us / 1000.0) == TUCAMRET_SUCCESS;
}

void CameraTUCAM_p::fetchImageData()
{
    TUCAMRET ret = TUCAM_Buf_WaitForFrame(m_opCam.hIdxTUCam, &m_frame, 1);
    if (ret != TUCAMRET_SUCCESS) return;
    uint8_t * pBuffer = m_frame.pBuffer + m_frame.usHeader;
    VideoFrame frame(pBuffer, QSize(m_frame.usWidth, m_frame.usHeight), AV_PIX_FMT_BGR24);
    frame.setExtraInfo(m_imageCount++, QDateTime::currentDateTime());
    emit videoFrameReady(frame);
}

bool CameraTUCAM_p::stopGrabbing()
{
    m_pThread->stop();
    //delete pImageBuffer;
    return true;
}

bool CameraTUCAM_p::getFlip(bool &von, bool &hon)
{
    int nVer = 0, nHor = 0;
    von = false;
    hon = false;
    TUCAMRET ret1 = TUCAM_Capa_GetValue(m_opCam.hIdxTUCam, TUIDC_VERTICAL, &nVer);
    TUCAMRET ret2 = TUCAM_Capa_GetValue(m_opCam.hIdxTUCam, TUIDC_HORIZONTAL, &nHor);

    bool ret = (ret1 == TUCAMRET_SUCCESS && ret2 == TUCAMRET_SUCCESS);

    if(nVer) von = true;
    if(nHor) hon = true;
    return ret;
}

bool CameraTUCAM_p::setVFlip(bool on)
{
    TUCAMRET ret;
    if(on)
    {
        ret = TUCAM_Capa_SetValue(m_opCam.hIdxTUCam, TUIDC_VERTICAL, 1);
    }
    else
    {
        ret = TUCAM_Capa_SetValue(m_opCam.hIdxTUCam, TUIDC_VERTICAL, 0);
    }

    return ret == TUCAMRET_SUCCESS;
}

bool CameraTUCAM_p::setHFlip(bool on)
{
    TUCAMRET ret;
    if(on)
    {
        ret = TUCAM_Capa_SetValue(m_opCam.hIdxTUCam, TUIDC_HORIZONTAL, 1);
    }
    else
    {
        ret = TUCAM_Capa_SetValue(m_opCam.hIdxTUCam, TUIDC_HORIZONTAL, 0);
    }

    return TUCAMRET_SUCCESS == ret;
}

void CameraTUCAM_p::on_imageThreadFinished()
{
    TUCAM_Buf_AbortWait(m_opCam.hIdxTUCam);
    TUCAM_Cap_Stop(m_opCam.hIdxTUCam);
    TUCAM_Buf_Release(m_opCam.hIdxTUCam);
    qDebug() << "imageThreadFinished";
    m_isLive = false;
}


bool CameraTUCAM_p::showVCDPropertyPage()
{
    m_dialog->initNodeInfo();
    m_dialog->show();
    return false;
}


QRect CameraTUCAM_p::AOIRegion()
{
    TUCAM_ROI_ATTR roi;
    roi.bEnable = true;
    if(TUCAM_Cap_GetROI(m_opCam.hIdxTUCam, &roi) == TUCAMRET_SUCCESS)
    {
        unsigned int xOffset = roi.nHOffset;
        unsigned int yOffset = roi.nVOffset;
        unsigned int xWidth = roi.nWidth;
        unsigned int yHeight = roi.nHeight;
        return QRect(xOffset, yOffset, xWidth, yHeight);
    }
    return QRect();
}

bool CameraTUCAM_p::setAOIRegion(unsigned int xOffset, unsigned int yOffset, unsigned int xWidth, unsigned int yHeight)
{
    TUCAM_ROI_ATTR roi;
    roi.bEnable = true;
    roi.nHOffset = xOffset;
    roi.nVOffset = yOffset;
    roi.nWidth = xWidth;
    roi.nHeight = yHeight;

    return TUCAMRET_SUCCESS == TUCAM_Cap_SetROI(m_opCam.hIdxTUCam, roi);

}
bool CameraTUCAM_p::setAOIRegion(QRect rect)
{
    unsigned int xOffset = static_cast<unsigned int>(rect.left());
    unsigned int yOffset = static_cast<unsigned int>(rect.top());
    unsigned int xWidth = static_cast<unsigned int>(rect.width());
    unsigned int yHeight = static_cast<unsigned int>(rect.height());

    return setAOIRegion(xOffset, yOffset, xWidth, yHeight);
}


}
